/* timer.c */

#include "ptpd.h"
#include "uc_log.h"
#include <rtdevice.h>
#include <rtthread.h>

#define HWTIMER3_DEV_NAME "timer3"
#define MICROSECONDS_PER_SECONDS (1000000)
#define MAX_TIME_SEC (0xFFFFFFFF)
#define MAX_TIME_USEC (1000000)

static rt_device_t hw_dev = RT_NULL; /* 定时器设备句柄 */
static unsigned int g_elapsed;

static systime_t sys_time = {0, 0};

void sys_time_init(systime_t *time)
{
    if (!hw_dev)
    {
        TRACE_I("time3 not init");
        return;
    }
    sys_time.sec = time->sec;

    sys_time.usec = time->usec;

    if (sys_time.usec >= MICROSECONDS_PER_SECONDS)
    {
        sys_time.sec += (sys_time.usec / MICROSECONDS_PER_SECONDS);

        sys_time.usec = sys_time.usec % MICROSECONDS_PER_SECONDS;
    }

    rt_device_read(hw_dev, 0, &sys_time.timeout_s, sizeof(rt_hwtimerval_t));

    TRACE_I("sys time init %u:%u, %u:%u", sys_time.sec, sys_time.usec, sys_time.timeout_s.sec, sys_time.timeout_s.usec);
}

void sys_time_get(systime_t *time)
{
    unsigned int timer_cnt = 0, pass_cnt = 0;
    rt_hwtimerval_t timeout_s;
    unsigned int diff_s = 0, diff_us = 0;

    if (!hw_dev)
    {
        TRACE_I("time3 not init");
        return;
    }

    rt_device_read(hw_dev, 0, &timeout_s, sizeof(rt_hwtimerval_t));

    diff_s = timeout_s.sec - sys_time.timeout_s.sec;
    sys_time.sec += diff_s;

    if (timeout_s.usec >= sys_time.timeout_s.usec)
    {
        diff_us = timeout_s.usec - sys_time.timeout_s.usec;
    }
    else
    {
        diff_us = MAX_TIME_USEC - sys_time.timeout_s.usec + timeout_s.usec;
        sys_time.sec -= 1;
    }
    sys_time.usec += diff_us;
    sys_time.sec += sys_time.usec / MICROSECONDS_PER_SECONDS;
    sys_time.usec = sys_time.usec % MICROSECONDS_PER_SECONDS;

    time->sec = sys_time.sec;
    time->usec = sys_time.usec;

    time->timeout_s.sec = timeout_s.sec;
    time->timeout_s.usec = timeout_s.usec;
}

void sys_time_set(systime_t *time)
{
    sys_time.sec = time->sec;

    sys_time.usec = time->usec;
}

/* 定时器超时回调函数 */
static rt_err_t timeout_cb(rt_device_t dev, rt_size_t size)
{
    g_elapsed++;
    return 0;
}

void initTimer(void)
{
    rt_hwtimerval_t timeout_s; /* 定时器超时值 */
    rt_hwtimer_mode_t mode;    /* 定时器模式 */
    rt_uint32_t freq = 10000;  /* 计数频率 */

    /* 查找定时器设备 */
    hw_dev = rt_device_find(HWTIMER3_DEV_NAME);
    if (hw_dev == RT_NULL)
    {
        TRACE_I("hwtimer can't find %s device!", HWTIMER3_DEV_NAME);
        return;
    }

    /* 以读写方式打开设备 */
    if (RT_EOK != rt_device_open(hw_dev, RT_DEVICE_OFLAG_RDWR))
    {
        TRACE_I("open %s device failed!", HWTIMER3_DEV_NAME);
        return;
    }

    /* 设置超时回调函数 */
    rt_device_set_rx_indicate(hw_dev, timeout_cb);

    /* 设置计数频率(若未设置该项，默认为1Mhz 或 支持的最小计数频率) */
    rt_device_control(hw_dev, HWTIMER_CTRL_FREQ_SET, &freq);
    /* 设置模式为周期性定时器（若未设置，默认是HWTIMER_MODE_ONESHOT）*/
    mode = HWTIMER_MODE_PERIOD;
    if (RT_EOK != rt_device_control(hw_dev, HWTIMER_CTRL_MODE_SET, &mode))
    {
        TRACE_I("set mode failed!");
        return;
    }

    /* 设置定时器超时值为1us并启动定时器 */
    timeout_s.sec = 0;  /* 秒 */
    timeout_s.usec = 1000; /* 微秒 */
    if (rt_device_write(hw_dev, 0, &timeout_s, sizeof(rt_hwtimerval_t)) != sizeof(rt_hwtimerval_t))
    {
        TRACE_I("set timeout value failed");
        return;
    }

    TRACE_I("initTimer suc");
}

void timerUpdate(IntervalTimer *itimer)
{
    unsigned int i, delta;

    delta = g_elapsed;
    g_elapsed = 0;

    if (delta <= 0)
        return;

    for (i = 0; i < TIMER_ARRAY_SIZE; ++i)
    {
        if ((itimer[i].interval) > 0 && ((itimer[i].left) -= delta) <= 0)
        {
            itimer[i].left = itimer[i].interval;
            itimer[i].expire = RT_TRUE;
            TRACE_I("timerUpdate: timer %u expired", i);
        }
    }
}

void timerStop(UInteger16 index, IntervalTimer *itimer)
{
    if (index >= TIMER_ARRAY_SIZE)
        return;

    itimer[index].interval = 0;
}

void timerStart(UInteger16 index, float interval, IntervalTimer *itimer)
{
    if (index >= TIMER_ARRAY_SIZE)
        return;

    itimer[index].expire = RT_FALSE;
    itimer[index].left = interval * 1000; // Factor 1000 used because resolution time is ms for the variable "elasped"
    itimer[index].interval = itimer[index].left;

    TRACE_I("timerStart: set timer %d to %d", index, interval);
}

Boolean timerExpired(UInteger16 index, IntervalTimer *itimer)
{
    timerUpdate(itimer);

    if (index >= TIMER_ARRAY_SIZE)
        return RT_FALSE;

    if (!itimer[index].expire)
        return RT_FALSE;

    itimer[index].expire = RT_FALSE;

    return RT_TRUE;
}
